﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GraphLibrary
{
    public class UndirectedGraph<TVertex, TEdge> : Graph<TVertex, TEdge>, IUndirectedGraph<TVertex, TEdge>
        where TEdge : IEdge<TVertex>
    {
        //ребра, входящие в данную вершину
        private Dictionary<TVertex, List<TEdge>> _vertexInEdges = new Dictionary<TVertex, List<TEdge>>();

        public override IEnumerable<TVertex> AdjacentVertices(TVertex vertex)
        {
            if (ContainsVertex(vertex))
            {
                List<TVertex> adjacentVertices = base.AdjacentVertices(vertex).ToList();
                foreach (TEdge edge in _vertexInEdges[vertex])
                {
                    adjacentVertices.Add(edge.Source);
                }
                return adjacentVertices.AsReadOnly();
            }
            else
                throw new GraphException("There is no such vertex in the graph");
        }

        public override IEnumerable<TEdge> IncidentEdges(TVertex vertex)
        {
            List<TEdge> incidentEdges = new List<TEdge>();
            incidentEdges.AddRange(base.IncidentEdges(vertex));
            incidentEdges.AddRange(_vertexInEdges[vertex]);
            return incidentEdges;
        }

        public override void AddVertex(TVertex vertex)
        {
            base.AddVertex(vertex);
            _vertexInEdges.Add(vertex, new List<TEdge>());
        }

        public override void AddVertices(IEnumerable<TVertex> vertices)
        {
            base.AddVertices(vertices);
            foreach (TVertex vertex in vertices)
            {
                _vertexInEdges.Add(vertex, new List<TEdge>());
            }
        }

        public override void AddEdge(TEdge edge)
        {
            if (!ContainsEdge(edge.Source, edge.Destination))
            {
                base.AddEdge(edge);
                _vertexInEdges[edge.Destination].Add(edge);
            }
            else
                throw new GraphException("This edge is already in the graph");
        }

        public override void AddEdges(IEnumerable<TEdge> edges)
        {
            base.AddEdges(edges);
            foreach (TEdge edge in edges)
            {
                _vertexInEdges[edge.Destination].Add(edge);
            }
        }

        protected override void RmVertex(TVertex vertex)
        {
            base.RmVertex(vertex);
            foreach (TVertex v in _vertexInEdges.Keys)
            {
                for (int i = 0; i < _vertexInEdges[v].Count; i++)
                {
                    if (_vertexInEdges[v][i].Source.Equals(vertex))
                    {
                        _vertexInEdges[v].Remove(_vertexInEdges[v][i]);
                        break;
                    }
                }
            }
            _vertexInEdges.Remove(vertex);
        }

        public override void RemoveEdge(TEdge edge)
        {
            base.RemoveEdge(edge);
            _vertexInEdges[edge.Destination].Remove(edge);
        }

        protected override bool RmEdge(TVertex source, TVertex destination)
        {
            if (!base.RmEdge(source, destination))
                return false;
            foreach (TEdge edge in _vertexInEdges[destination])
            {
                if (edge.Source.Equals(source))
                {
                    _vertexInEdges[destination].Remove(edge);
                    return true;
                }
            }
            return false;
        }

        public override void RemoveEdge(TVertex source, TVertex destination)
        {
            if (ContainsEdge(source, destination))
            {
                if (!RmEdge(source, destination))
                {
                    RmEdge(destination, source);
                }
            }
            else
            {
                throw new GraphException("Graph doesn't contains this edge");
            }
        }

        public override void RemoveEdges(IEnumerable<TEdge> edges)
        {
            base.RemoveEdges(edges);
            foreach (TEdge edge in edges)
            {
                _vertexInEdges[edge.Destination].Remove(edge);
            }
        }

        private bool FindEdge(TVertex source, TVertex destination)
        {
            if (_vertexInEdges.ContainsKey(destination))
                if (_vertexInEdges[destination].Find(e => e.Source.Equals(source)) != null)
                    return true;
            if (base.ContainsEdge(source, destination))
                return true;
            return false;
        }

        public override bool ContainsEdge(TEdge edge)
        {
            return ContainsEdge(edge.Source, edge.Destination);
        }

        public override bool ContainsEdge(TVertex source, TVertex destination)
        {
            return FindEdge(source, destination) || FindEdge(destination, source);
        }

        public override double[,] ToAgencyMatrix(Func<TEdge, double> weight)
        {
            if (weight == null)
            {
                weight = e => 1;
            }
            double[,] matrix = base.ToAgencyMatrix(weight);
            List<TVertex> vertices = Vertices.ToList();
            for (int i = 0; i < _vertexInEdges.Count; i++)
            {
                foreach (TEdge edge in _vertexInEdges[vertices[i]])
                {
                    matrix[i, vertices.IndexOf(edge.Source)] = weight(edge);
                }
            }
            return matrix;
        }
    }
}
